/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "halls_of_lightning.h"
#include "ScriptedEscortAI.h"

enum BjarngrimSpells
{
	// DEFENSIVE STANCE
	SPELL_DEFENSIVE_STANCE				= 53790,
	SPELL_DEFENSIVE_AURA				= 41105,
	SPELL_BJARNGRIM_REFLETION			= 36096,
	SPELL_PUMMEL						= 12555,
	SPELL_KNOCK_AWAY					= 52029,
	SPELL_IRONFORM						= 52022,

	// BERSERKER STANCE
	SPELL_BERSERKER_STANCE				= 53791,
	SPELL_BERSERKER_AURA				= 41107,
	SPELL_MORTAL_STRIKE					= 16856,
	SPELL_WHIRLWIND						= 52027,
	
	// BATTLE STANCE
	SPELL_BATTLE_STANCE					= 53792,
	SPELL_BATTLE_AURA					= 41106,
	SPELL_INTERCEPT						= 58769,
	SPELL_CLEAVE						= 15284,
	SPELL_SLAM							= 52026,

	//OTHER SPELLS
	SPELL_CHARGE_UP						= 52098,	  // only used when starting walk from one platform to the other
	SPELL_TEMPORARY_ELECTRICAL_CHARGE	= 52092,	  // triggered part of above

	// STORMFORGED LIEUTENANT
	SPELL_ARC_WELD						= 59085,
	SPELL_RENEW_STEEL_N					= 52774,
	SPELL_RENEW_STEEL_H					= 59160,
};

enum BjarngrimOther
{
	// Stances
	STANCE_DEFENSIVE					= 1,
	STANCE_BERSERKER					= 2,
	STANCE_BATTLE						= 3,

	// NPCs
	NPC_STORMFORGED_LIEUTENANT			= 29240,

	// Models
	EQUIP_SWORD							= 37871,
	EQUIP_SHIELD						= 35642,
	EQUIP_MACE							= 43623,
};

enum BjarngrimEvents
{
	EVENT_BJARNGRIM_CHANGE_STANCE		= 1,

	// DEFENSIVE STANCE
	EVENT_BJARNGRIM_REFLECTION			= 11,
	EVENT_BJARNGRIM_PUMMEL				= 12,
	EVENT_BJARNGRIM_KNOCK				= 13,
	EVENT_BJARNGRIM_IRONFORM			= 14,

	// BERSERKER STANCE
	EVENT_BJARNGRIM_MORTAL_STRIKE		= 21,
	EVENT_BJARNGRIM_WHIRLWIND			= 22,

	// BATTLE STANCE
	EVENT_BJARNGRIM_INTERCEPT			= 31,
	EVENT_BJARNGRIM_CLEAVE				= 32,
	EVENT_BJARNGRIM_SLAM				= 33,

	// STORMFORGED LIEUTENANT
	EVENT_ARC_WELD						= 41,
	EVENT_RENEW_STEEL					= 42,
};

enum Yells
{
    SAY_AGGRO                               = 0,
    SAY_DEFENSIVE_STANCE                    = 1,
    SAY_BATTLE_STANCE                       = 2,
    SAY_BERSERKER_STANCE                    = 3,
    SAY_SLAY                                = 4,
    SAY_DEATH                               = 5,
    EMOTE_DEFENSIVE_STANCE                  = 6,
    EMOTE_BATTLE_STANCE                     = 7,
    EMOTE_BERSEKER_STANCE                   = 8,
};

class boss_bjarngrim : public CreatureScript
{
public:
    boss_bjarngrim() : CreatureScript("boss_bjarngrim") { }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_bjarngrimAI (creature);
    }

	struct boss_bjarngrimAI : public npc_escortAI
	{
		boss_bjarngrimAI(Creature* creature) : npc_escortAI(creature), summons(creature)
		{
			m_pInstance = creature->GetInstanceScript();

			// Init waypoints
			AddWaypoint(1, 1262.18f, 99.3f, 33.5f, 0);
			AddWaypoint(2, 1281.6f, 99.5f, 33.5f, 0);
			AddWaypoint(3, 1311.7f, 99.4f, 40.1f, 0);
			AddWaypoint(4, 1332.5f, 99.7f, 40.18f, 0);
			AddWaypoint(5, 1311.7f, 99.4f, 40.1f, 0);
			AddWaypoint(6, 1281.6f, 99.5f, 33.5f, 0);
			AddWaypoint(7, 1262.18f, 99.3f, 33.5f, 0);
			AddWaypoint(8, 1262, -26.9f, 33.5f, 0);
			AddWaypoint(9, 1281.2f, -26.8f, 33.5f, 0);
			AddWaypoint(10, 1311.3f, -26.9f, 40.03f, 0);
			AddWaypoint(11, 1332, -26.6f, 40.18f, 0);
			AddWaypoint(12, 1311.3f, -26.9f, 40.03f, 0);
			AddWaypoint(13, 1281.2f, -26.8f, 33.5f, 0);
			AddWaypoint(14, 1262, -26.9f, 33.5f, 0);
			
			Start(true, false, 0, NULL, false, true);
		}

		InstanceScript* m_pInstance;
		EventMap events;
		SummonList summons;
		uint8 m_uiStance;

		void Reset()
		{
			events.Reset();
			summons.DespawnAll();

			for (uint8 i = 0; i < 2; ++i)
				if (Creature* dwarf = me->SummonCreature(NPC_STORMFORGED_LIEUTENANT, me->GetPositionX()+urand(4,12), me->GetPositionY()+urand(4,12), me->GetPositionZ()))
				{
					dwarf->GetMotionMaster()->MoveFollow(me, 3, rand_norm()*2*3.14f);
					summons.Summon(dwarf);
				}

			me->RemoveAllAuras();
			me->CastSpell(me, SPELL_TEMPORARY_ELECTRICAL_CHARGE, true);
			RollStance(0, STANCE_DEFENSIVE);

			if (m_pInstance)
				m_pInstance->SetData(TYPE_BJARNGRIM, NOT_STARTED);
		}

		void EnterCombat(Unit*)
		{
			me->SetInCombatWithZone();
			Talk(SAY_AGGRO);

			events.ScheduleEvent(EVENT_BJARNGRIM_CHANGE_STANCE, 20000, 0);

			// DEFENSIVE STANCE
			events.ScheduleEvent(EVENT_BJARNGRIM_REFLECTION, 8000, STANCE_DEFENSIVE);
			events.ScheduleEvent(EVENT_BJARNGRIM_PUMMEL, 5000, STANCE_DEFENSIVE);
			events.ScheduleEvent(EVENT_BJARNGRIM_KNOCK, 16000, STANCE_DEFENSIVE);
			events.ScheduleEvent(EVENT_BJARNGRIM_IRONFORM, 12000, STANCE_DEFENSIVE);

			// BERSERKER STANCE
			events.ScheduleEvent(EVENT_BJARNGRIM_MORTAL_STRIKE, 20000+4000, STANCE_BERSERKER);
			events.ScheduleEvent(EVENT_BJARNGRIM_WHIRLWIND, 20000+6000, STANCE_BERSERKER);

			// BATTLE STANCE
			events.ScheduleEvent(EVENT_BJARNGRIM_INTERCEPT, 20000+3000, STANCE_BATTLE);
			events.ScheduleEvent(EVENT_BJARNGRIM_CLEAVE, 20000+5000, STANCE_BATTLE);
			events.ScheduleEvent(EVENT_BJARNGRIM_SLAM, 20000+10000, STANCE_BATTLE);

			if (m_pInstance)
			{
				m_pInstance->SetData(TYPE_BJARNGRIM, IN_PROGRESS);
				m_pInstance->SetData(DATA_BJARNGRIM_ACHIEVEMENT, me->HasAura(SPELL_TEMPORARY_ELECTRICAL_CHARGE));
			}
		}

		void KilledUnit(Unit* victim)
		{
			if (victim->GetTypeId() != TYPEID_PLAYER)
				return;

			Talk(SAY_SLAY);
		}

		void JustDied(Unit*)
		{
			Talk(SAY_DEATH);

			if (m_pInstance)
				m_pInstance->SetData(TYPE_BJARNGRIM, DONE);
		}

		void RemoveStanceAura(uint8 stance)
		{
			switch (stance)
			{
				case STANCE_DEFENSIVE:
					me->RemoveAura(SPELL_DEFENSIVE_STANCE);
					me->RemoveAura(SPELL_DEFENSIVE_AURA);
					break;
				case STANCE_BERSERKER:
					me->RemoveAura(SPELL_BERSERKER_STANCE);
					me->RemoveAura(SPELL_BERSERKER_AURA);
					break;
				case STANCE_BATTLE:
					me->RemoveAura(SPELL_BATTLE_STANCE);
					me->RemoveAura(SPELL_BATTLE_AURA);
					break;
			}
		}

		void RollStance(uint8 stance, uint8 force = 0)
		{
			if (urand(0,1))
				stance = (++stance == 4 ? 1 : stance);
			else
				stance = (--stance == 0 ? 3 : stance);

			if (force)
				stance = force;
			
			switch (stance)
			{
				case STANCE_DEFENSIVE:
					Talk(SAY_DEFENSIVE_STANCE);

					me->CastSpell(me, SPELL_DEFENSIVE_STANCE, true);
					me->CastSpell(me, SPELL_DEFENSIVE_AURA, true);

					events.DelayEvents(20000, STANCE_BERSERKER);
					events.DelayEvents(20000, STANCE_BATTLE);

					SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SHIELD, EQUIP_NO_CHANGE);
					break;
				case STANCE_BERSERKER:
					Talk(SAY_BERSERKER_STANCE);

					me->CastSpell(me, SPELL_BERSERKER_STANCE, true);
					me->CastSpell(me, SPELL_BERSERKER_AURA, true);

					events.DelayEvents(20000, STANCE_DEFENSIVE);
					events.DelayEvents(20000, STANCE_BATTLE);

					SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SWORD, EQUIP_NO_CHANGE);
					break;
				case STANCE_BATTLE:
					Talk(SAY_BATTLE_STANCE);

					me->CastSpell(me, SPELL_BATTLE_STANCE, true);
					me->CastSpell(me, SPELL_BATTLE_AURA, true);

					events.DelayEvents(20000, STANCE_BERSERKER);
					events.DelayEvents(20000, STANCE_DEFENSIVE);

					SetEquipmentSlots(false, EQUIP_MACE, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
					break;
			}

			m_uiStance = stance;
		}

		void WaypointReached(uint32 Point)
		{
			if (Point == 1 || Point == 8)
				me->CastSpell(me, SPELL_TEMPORARY_ELECTRICAL_CHARGE, true);
			else if (Point == 7 || Point == 14)
				me->RemoveAura(SPELL_TEMPORARY_ELECTRICAL_CHARGE);
		}

		void UpdateEscortAI(uint32 diff)
		{
			if (!me->IsInCombat())
				return;

			// Return since we have no target
			if (!UpdateVictim())
			{
				Reset();
				return;
			}

			events.Update(diff);

			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_BJARNGRIM_CHANGE_STANCE:
					// roll new stance
					RemoveStanceAura(m_uiStance);
					RollStance(m_uiStance);
					events.RepeatEvent(20000);
					break;

				///////////////////////////////////////////////////////
				///// DEFENSIVE STANCE
				///////////////////////////////////////////////////////
				case EVENT_BJARNGRIM_REFLECTION:
					me->CastSpell(me, SPELL_BJARNGRIM_REFLETION, true);
					events.RepeatEvent(8000 + rand()%1000);
					break;
				case EVENT_BJARNGRIM_PUMMEL:
					me->CastSpell(me->GetVictim(), SPELL_PUMMEL, false);
					events.RepeatEvent(10000 + rand()%1000);
					break;
				case EVENT_BJARNGRIM_KNOCK:
					me->CastSpell(me, SPELL_KNOCK_AWAY, false);
					events.RepeatEvent(20000 + rand()%1000);
					break;
				case EVENT_BJARNGRIM_IRONFORM:
					me->CastSpell(me, SPELL_IRONFORM, true);
					events.RepeatEvent(18000 + rand()%5000);
					break;

				///////////////////////////////////////////////////////
				///// BERSERKER STANCE
				///////////////////////////////////////////////////////
				case EVENT_BJARNGRIM_MORTAL_STRIKE:
					me->CastSpell(me->GetVictim(), SPELL_MORTAL_STRIKE, false);
					events.RepeatEvent(10000);
					break;
				case EVENT_BJARNGRIM_WHIRLWIND:
					me->CastSpell(me, SPELL_WHIRLWIND, true);
					events.RepeatEvent(25000);
					break;
					
				///////////////////////////////////////////////////////
				///// BATTLE STANCE
				///////////////////////////////////////////////////////
				case EVENT_BJARNGRIM_INTERCEPT:
					if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
						me->CastSpell(target, SPELL_INTERCEPT, true);

					events.RepeatEvent(30000);
					break;
				case EVENT_BJARNGRIM_CLEAVE:
					me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
					events.RepeatEvent(25000);
					break;
				case EVENT_BJARNGRIM_SLAM:
					me->CastSpell(me->GetVictim(), SPELL_SLAM, false);
					events.RepeatEvent(10000 + rand()%2000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class npc_stormforged_lieutenant : public CreatureScript
{
public:
    npc_stormforged_lieutenant() : CreatureScript("npc_stormforged_lieutenant") { }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new npc_stormforged_lieutenantAI (creature);
    }

	struct npc_stormforged_lieutenantAI : public ScriptedAI
	{
		npc_stormforged_lieutenantAI(Creature* creature) : ScriptedAI(creature) { }

		EventMap events;
		uint64 BjarngrimGUID;

		void Reset()
		{
			if (me->IsSummon())
				BjarngrimGUID = me->ToTempSummon()->GetSummonerGUID();
			else
				BjarngrimGUID = 0;
		}

		void EnterCombat(Unit*)
		{
			events.ScheduleEvent(EVENT_ARC_WELD, 2000);
			events.ScheduleEvent(EVENT_RENEW_STEEL, 10000 + rand()%1000);
		}

		void UpdateAI(uint32 diff)
		{
			//Return since we have no target
			if (!UpdateVictim())
				return;

			events.Update(diff);

			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_ARC_WELD:
					me->CastSpell(me->GetVictim(), SPELL_ARC_WELD, true);
					events.RepeatEvent(20000);
					break;
				case EVENT_RENEW_STEEL:
					if (Creature* bjarngrim = ObjectAccessor::GetCreature(*me, BjarngrimGUID))
						if (bjarngrim->IsAlive())
							me->CastSpell(bjarngrim, me->GetMap()->IsHeroic() ? SPELL_RENEW_STEEL_H : SPELL_RENEW_STEEL_N, true);

					events.RepeatEvent(10000 + rand()%4000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};


void AddSC_boss_bjarngrim()
{
	new boss_bjarngrim();
	new npc_stormforged_lieutenant();
}
